package world;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;
import java.awt.Point;
import java.io.*;

public class Map {
	
	private int[][] grid;
	private int size;	//zakladam,ze mapa jest kwadratowa,ale nie problem to zmienic
	private boolean playerNearWall;
	private boolean wumpusDeathScreamEcho;
	
	public Map() { this(6); }
	public Map(int rozmiar) {
		size = rozmiar;
		grid = new int[rozmiar][rozmiar];
		playerNearWall = false;
		wumpusDeathScreamEcho = false;
	}
	
	//Konstruktor, ktory wczytuje mape i umiejscowienie elementow z pliku
	public Map(String f) {
		int x=0, y=0;
		playerNearWall = false;
		wumpusDeathScreamEcho = false;
		Vector<Integer> v = new Vector<Integer>(1);
		try {	
			File file = new File(f);
			Scanner scanner = new Scanner(file);
			while (scanner.hasNextLine()){
				Scanner sl = new Scanner(scanner.nextLine());
				//TODO dodać z tabulatorami...
				//sl.useDelimiter(Character.toString('\u0009'));
				sl.useDelimiter(" ");
		        while (sl.hasNext()) {
		        	if (x==0) {
		        		v.add(sl.nextInt());
		        	} else {
		        		grid[x][y++] = sl.nextInt();
		        	}	
		        }
		        sl.close();	
		        if (x==0) {
		        	grid = new int[v.size()][v.size()];
		        	size = v.size();
		        	for (int i = 0; i<v.size(); i++) {
		        		grid[0][i]= v.get(i);
		        	}
		        }
		        x++;
		        y=0;
			}
			scanner.close();
		} catch (FileNotFoundException e) {
		    e.printStackTrace();
		}
	}

	public int getSize() {
		return size;
	}
	public int getField(int x, int y) {
		return grid[y][x];
	}
	public int getField(int[] coord) {
		return grid[coord[1]][coord[0]];
	}
	public int getField(Point coord) {
		return grid[(int)coord.getY()][(int)coord.getX()];
	}
	public int[] getRow(int y) {
		return grid[y];
	}
	public int[][] getGrid() {
		return grid;
	}	
	public boolean isPlayerNearWall() {
		return playerNearWall;
	}
	public boolean isWumpusDeathScreamEcho() {
		return wumpusDeathScreamEcho;
	}

	
	public void setFieldDel(int x, int y, int value) {
		grid[y][x] = value;
	}
	public void setFieldDel(int x, int y, FieldValues value) {
		grid[y][x] = value.getNumber();
	}	
	public void setFieldDel(Point p, FieldValues value) {
		grid[(int)p.getY()][(int)p.getX()] = value.getNumber();
	}	
	public void setFieldDel(Point p, int value) {
		grid[(int)p.getY()][(int)p.getX()] = value;
	}
	
	public void setField(int x, int y, int value) {
		grid[y][x] *= value;
	}
	public void setField(int x, int y, FieldValues value) {
		grid[y][x] *= value.getNumber();
	}	
	public void setField(Point p, FieldValues value) {
		grid[(int)p.getY()][(int)p.getX()] *= value.getNumber();
	}	
	public void setField(Point p, int value) {
		grid[(int)p.getY()][(int)p.getX()] *= value;
	}
	public void setField(int[] coord, int value) {
		grid[coord[1]][coord[0]] *= value;
	}
	public void setGrid(int[][] g) {
		grid = g;
	}
	public void setPlayerNearWall(boolean playerNearWall) {
		this.playerNearWall = playerNearWall;
	}	
	public void setWumpusDeathScreamEcho(boolean wumpusDeathScreamEcho) {
		this.wumpusDeathScreamEcho = wumpusDeathScreamEcho;
	}
	
	public void removeTarget(int x, int y, FieldValues fv) {
		setFieldDel(x, y, getField(x, y)/fv.getNumber());
	}
	
	public void removeTarget(Point p, FieldValues fv) {
		int x = (int)p.getX();
		int y = (int)p.getY();
		setFieldDel(x, y, getField(x, y)/fv.getNumber());
	}
	
	public void removeTarget(Point p, int value) {
		int x = (int)p.getX();
		int y = (int)p.getY();
		int zm = getField(x, y)/value;
		setFieldDel(x, y, zm);
	}
	
	public boolean checkValue(int x, int y, FieldValues fv) {
		return (getField(x, y)%fv.getNumber() == 0);
	}
	
	public boolean outOfMap(int x, int y) {
		if (x < 0 || x >= getSize() || y < 0 || y >= getSize())
			return true;
		else
			return false;
	}
	
	public boolean shotWumpus(int x, int y, FieldValues fv) {
		int i=0;
		switch (fv) {
		case UP:
			for (i = y; i<getSize(); i++) {
				if (checkValue(x, i, FieldValues.WUMPUS)) {
					removeTarget(x, i, FieldValues.WUMPUS);
					if (!outOfMap(x+1, i))
						removeTarget(x+1, i, FieldValues.STENCH);
					if (!outOfMap(x-1, i))
						removeTarget(x-1, i, FieldValues.STENCH);
					if (!outOfMap(x, i+1))
						removeTarget(x, i+1, FieldValues.STENCH);
					if (!outOfMap(x, i-1))
						removeTarget(x, i-1, FieldValues.STENCH);
					return true;
				}
			}
			break;
		case DOWN:
			for (i = y; i>=0; i--) {
				if (checkValue(x, i, FieldValues.WUMPUS)) {
					removeTarget(x, i, FieldValues.WUMPUS);
					if (!outOfMap(x+1, i))
						removeTarget(x+1, i, FieldValues.STENCH);
					if (!outOfMap(x-1, i))
						removeTarget(x-1, i, FieldValues.STENCH);
					if (!outOfMap(x, i+1))
						removeTarget(x, i+1, FieldValues.STENCH);
					if (!outOfMap(x, i-1))
						removeTarget(x, i-1, FieldValues.STENCH);
					return true;
				}
			}
			break;
		case RIGHT:
			for (i = x; i<getSize(); i++) {
				if (checkValue(i, y, FieldValues.WUMPUS)) {
					removeTarget(i, y, FieldValues.WUMPUS);
					if (!outOfMap(i, y+1))
						removeTarget(i, y+1, FieldValues.STENCH);
					if (!outOfMap(i, y-1))
						removeTarget(i, y-1, FieldValues.STENCH);
					if (!outOfMap(i+1, y))
						removeTarget(i+1, y, FieldValues.STENCH);
					if (!outOfMap(i-1, y))
						removeTarget(i-1, y, FieldValues.STENCH);
					return true;
				}
			}
			break;
		case LEFT:
			for (i = x; i>=0; i--) {
				if (checkValue(i, y, FieldValues.WUMPUS)) {
					removeTarget(i, y, FieldValues.WUMPUS);
					if (!outOfMap(x+1, i))
						removeTarget(x+1, i, FieldValues.STENCH);
					if (!outOfMap(x-1, i))
						removeTarget(x-1, i, FieldValues.STENCH);
					if (!outOfMap(x, i+1))
						removeTarget(x, i+1, FieldValues.STENCH);
					if (!outOfMap(x, i-1))
						removeTarget(x, i-1, FieldValues.STENCH);
					return true;
				}
			}
			break;
		}
		return false;
	}
	
	public boolean bumpWall(Point p) {
		if (p.getX() < 0 || p.getX() >= getSize() || p.getY() < 0 || p.getY() >= getSize())
			return true;
		else
			return false;
	}
	
	public void print() {
		
		System.out.println(Arrays.deepToString(getGrid()));
	}
	public void print2() {
		int i = 0;
		int j = 0;
		while (i < getSize()) {
			while (j < getSize()) {
				System.out.print(getField(i, j)+" ");
				++j;
			}
			System.out.print("\n");
			++i;
			j=0;
		}
	}
	
	//W tablicy jedynek reprezentujacych mape dziury sa reprezentowane przez 5, a wiatr przez 7
	//Obwarowane warunkami, gdy zostanie wybrane pole na obrzezach mapy
	//TODO Zoptymalizowac
	public void insertHoles(int x, int y) {
		setField(x, y, FieldValues.PIT);
		if (x==0) {
			setField(x+1, y, FieldValues.BREEZE);
		}
		else if (x==size-1){
			setField(x-1, y, FieldValues.BREEZE);
		}
		else {
			setField(x-1, y, FieldValues.BREEZE);
			setField(x+1, y, FieldValues.BREEZE);
		}
		if (y==0) {
			setField(x, y+1, FieldValues.BREEZE);
		}
		else if (y==size-1){
			setField(x, y-1, FieldValues.BREEZE);
		}
		else {
			setField(x, y-1, FieldValues.BREEZE);
			setField(x, y+1, FieldValues.BREEZE);
		}
	}
	
	//W tablicy jedynek reprezentujacych mape Wumpus jest reprezentowany przez 3, a smrod przez 11
	//Obwarowane warunkami, gdy zostanie wybrane pole na obrzezach mapy
	public void insertWumpus(int x, int y) {
		setField(x, y, FieldValues.WUMPUS);
		if (x==0) {
			setField(x+1, y, FieldValues.STENCH);
		}
		else if (x==size-1){
			setField(x-1, y, FieldValues.STENCH);
		}
		else {
			setField(x-1, y, FieldValues.STENCH);
			setField(x+1, y, FieldValues.STENCH);
		}
		if (y==0) {
			setField(x, y+1, FieldValues.STENCH);
		}
		else if (y==size-1){
			setField(x, y-1, FieldValues.STENCH);
		}
		else {
			setField(x, y-1, FieldValues.STENCH);
			setField(x, y+1, FieldValues.STENCH);
		}
	}
	
	//W tablicy jedynek reprezentujacych mape gracz jest reprezentowany przez -1
	public void insertPlayer(int x, int y, FieldValues fv) {
		setField(x, y, FieldValues.AGENT.getNumber()*fv.getNumber());
	}
	
	public void insertGold(int x, int y) {
		setField(x, y, FieldValues.GOLD);
	}
	
	public void emptyField(int x, int y) {
		setField(x, y, FieldValues.EMPTY);
	}
	
	boolean coverHole(int x, int y) {
		if (checkValue(x, y, FieldValues.PIT))	{
			removeTarget(x, y, FieldValues.PIT);
			if (!outOfMap(x+1, y))
				removeTarget(x+1, y, FieldValues.BREEZE);
			if (!outOfMap(x-1, y))
				removeTarget(x-1, y, FieldValues.BREEZE);
			if (!outOfMap(x, y+1))
				removeTarget(x, y+1, FieldValues.BREEZE);
			if (!outOfMap(x, y-1))
				removeTarget(x, y-1, FieldValues.BREEZE);
			return true;
		} else {
			return false;
		}
	}
	
	//Losowe rozmieszczenie gracza, Wumpusa i 3 dziur gdyby nie zostal podany plik wejsciowy
	//Zabezpieczony przed nakladaniem sie na siebie dziur i Wumpusa
	public void createRandomMap() {
		Random r =new Random();
		int x, y;
		int i=3;
		do {
			x=r.nextInt(getSize())-1;
			y=r.nextInt(getSize())-1;
			if (getField(x,y)!=FieldValues.PIT.getNumber()) {
				insertHoles(x,y);
				--i;
			}
		} while (i>0);
		do {
			x=r.nextInt(getSize())-1;
			y=r.nextInt(getSize())-1;
			if (getField(x, y)!=FieldValues.PIT.getNumber()) {
				insertWumpus(x, y);
				++i;
			} 
		} while (i<1);
		do {
			x=r.nextInt(getSize())-1;
			y=r.nextInt(getSize())-1;
			if (getField(x, y)!=FieldValues.PIT.getNumber() 
					&& getField(x, y)!=FieldValues.WUMPUS.getNumber()) {
				insertPlayer(x, y, FieldValues.RIGHT);
				--i;
			} 
		} while (i>0);		
	}	
	
	public Player updateMap(Action a, Player p) {
		setPlayerNearWall(false);
		setWumpusDeathScreamEcho(false);
		switch (a) {
		case TURN_LEFT:	
			removeTarget(p.getPosition(), p.getDirection());
			p.turn(Action.TURN_LEFT);
			setField(p.getPosition(), p.getDirection());
			break;
		case TURN_RIGHT:
			removeTarget(p.getPosition(), p.getDirection());
			p.turn(Action.TURN_RIGHT);
			setField(p.getPosition(), p.getDirection());
			break;
		case MOVE:
			Point d = new Point(p.getPosition());
			p.move();
			if (bumpWall(p.getPosition())) {
				p.setPosition(d);
				setPlayerNearWall(true);
			}
			removeTarget(d, FieldValues.AGENT.getNumber() * p.getDirection().getNumber());
			setField(p.getPosition(), FieldValues.AGENT.getNumber() * p.getDirection().getNumber());
			break;
		case GRAB:
			removeTarget(p.getPosition(), FieldValues.GOLD);
			break;
		case DROP:
			setField(p.getPosition(), FieldValues.GOLD);
			break;
		case SHOOT:
			setWumpusDeathScreamEcho(shotWumpus((int)p.getPosition().getX(), 
					(int)p.getPosition().getY(), p.shoot()));
			break;
		case COVER:
			p.coverHole();
			FieldValues v = p.getDirection();
			switch (v) {
			case UP:
				coverHole((int)p.getPosition().getX(), (int)p.getPosition().getY()-1);
				break;
			case DOWN:
				coverHole((int)p.getPosition().getX(), (int)p.getPosition().getY()+1);
				break;
			case RIGHT:
				coverHole((int)p.getPosition().getX()+1, (int)p.getPosition().getY());
				break;
			case LEFT:
				coverHole((int)p.getPosition().getX()-1, (int)p.getPosition().getY()-1);
				break;
			default:
				break;
			}
			break;
		case EXIT:
			removeTarget(p.getPosition(), FieldValues.AGENT);
			p.setPosition(new Point(0,0));
			p.setDirection(FieldValues.EMPTY);
			break;
		case DIE:
			removeTarget(p.getPosition(), FieldValues.AGENT);
			p.setPosition(new Point(-1,-1));
			p.setDirection(FieldValues.EMPTY);
			break;
		}
		return p;
	}
}
